<script setup>
import * as Three from "three";
import { onMounted, ref } from "vue";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { CSG } from "three-csg-ts";

// 添加响应式状态
const isClipping = ref(false);
let control = null;
let scene, mesh, camera, renderer;
let raycaster, mouse;
let isDrawing = false;
let drawingPoints = [];
let drawingMesh = null;
let originalMesh = null;

function init() {
  // 创建场景
  scene = new Three.Scene();
  // 创建模型外观
  const geometry = new Three.BoxGeometry(10, 10, 10);
  // 创建材质
  const material = new Three.MeshLambertMaterial({
    color: "red",
  });
  // 创建模型
  mesh = new Three.Mesh(geometry, material);
  originalMesh = mesh.clone();
  // 场景添加模型
  scene.add(mesh);

  //创建摄像机
  camera = new Three.PerspectiveCamera(45, 1, 0.1, 1000);
  // 设置相机位置
  camera.position.set(100, 100, 100);
  // 设置相机初始聚焦点
  camera.lookAt(mesh.position);

  //   创建点光源
  //   const pointLight = new Three.PointLight("#fff", 1.0);
  //   禁止光源衰减
  //   pointLight.decay = 0;
  //   pointLight.position.set(20, 20, 20);
  //   scene.add(pointLight);

  // 创建环境光
  const lightEvn = new Three.AmbientLight("#fff", 1);
  // 创建平行光
  const lightDir = new Three.DirectionalLight("#fff", 1);
  lightDir.position.set(20, 20, 20);
  scene.add(lightEvn);
  scene.add(lightDir);

  // 创建渲染器
  renderer = new Three.WebGLRenderer();
  // canvas画布大小
  renderer.setSize(800, 800);
  // 执行渲染
  renderer.render(scene, camera);
  // 将渲染添加到网页上
  document.getElementById("scene").appendChild(renderer.domElement);

  // 创建控制器
  control = new OrbitControls(camera, renderer.domElement);

  // 初始化射线投射器和鼠标向量
  raycaster = new Three.Raycaster();
  mouse = new Three.Vector2();

  function animate() {
    requestAnimationFrame(animate);
    renderer.render(scene, camera);
  }
  animate();
}

// 开启切割功能
function enableClipping() {
  if (isClipping.value) return;

  isClipping.value = true;
  // 禁用控制器
  control.enabled = false;

  // 添加鼠标事件监听器
  const canvas = renderer.domElement;
  canvas.addEventListener("mousedown", onMouseDown);
  canvas.addEventListener("mousemove", onMouseMove);
  canvas.addEventListener("mouseup", onMouseUp);
}

// 关闭切割功能
function disableClipping() {
  if (!isClipping.value) return;

  isClipping.value = false;
  // 重新启用控制器
  control.enabled = true;

  // 移除鼠标事件监听器
  const canvas = renderer.domElement;
  canvas.removeEventListener("mousedown", onMouseDown);
  canvas.removeEventListener("mousemove", onMouseMove);
  canvas.removeEventListener("mouseup", onMouseUp);

  // 清除绘制轨迹
  clearDrawingPath();
}

// 鼠标按下事件
function onMouseDown(event) {
  if (!isClipping.value) return;

  isDrawing = true;
  drawingPoints = [];

  // 清除之前的绘制轨迹
  clearDrawingPath();

  // 记录第一个点
  addDrawingPoint(event);
}

// 鼠标移动事件
function onMouseMove(event) {
  if (!isClipping.value || !isDrawing) return;

  // 添加轨迹点
  addDrawingPoint(event);

  // 更新绘制轨迹可视化
  updateDrawingPath();
}

// 鼠标释放事件
function onMouseUp() {
  if (!isClipping.value || !isDrawing) return;

  isDrawing = false;

  // 如果有足够的点，执行切割
  if (drawingPoints.length >= 3) {
    performClipping();
  }
}

// 添加绘制点
function addDrawingPoint(event) {
  // 计算鼠标在归一化设备坐标中的位置
  const rect = renderer.domElement.getBoundingClientRect();
  mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
  mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;

  // 从相机发射射线
  raycaster.setFromCamera(mouse, camera);

  // 检查射线是否与模型相交
  const intersects = raycaster.intersectObject(mesh);

  if (intersects.length > 0) {
    // 记录交点位置
    drawingPoints.push(intersects[0].point.clone());
  }
}

// 更新绘制轨迹可视化
function updateDrawingPath() {
  if (drawingPoints.length < 2) return;

  // 清除之前的轨迹
  if (drawingMesh) {
    scene.remove(drawingMesh);
  }

  // 创建闭合轨迹
  const points = [...drawingPoints];
  if (points.length > 2) {
    points.push(points[0].clone()); // 闭合路径
  }

  // 创建轨迹几何体
  const geometry = new Three.BufferGeometry().setFromPoints(points);
  const material = new Three.LineBasicMaterial({ color: 0xffff00 });
  drawingMesh = new Three.Line(geometry, material);
  scene.add(drawingMesh);
}

// 清除绘制轨迹
function clearDrawingPath() {
  if (drawingMesh) {
    scene.remove(drawingMesh);
    drawingMesh = null;
  }
}

// 执行切割操作
function performClipping() {
  // 创建一个挤出几何体，基于用户绘制的轨迹
  const shape = new Three.Shape();

  // 设置起点
  shape.moveTo(drawingPoints[0].x, drawingPoints[0].y);

  // 添加所有点
  for (let i = 1; i < drawingPoints.length; i++) {
    shape.lineTo(drawingPoints[i].x, drawingPoints[i].y);
  }

  // 闭合形状
  shape.closePath();

  // 计算挤出方向（使用相机方向的反方向）
  const extrudeDirection = new Three.Vector3();
  camera.getWorldDirection(extrudeDirection);
  extrudeDirection.negate().multiplyScalar(20); // 挤出长度

  // 创建挤出几何体
  const extrudeSettings = {
    steps: 1,
    depth: 2000,
    bevelEnabled: false,
  };

  const extrudeGeometry = new Three.ExtrudeGeometry(shape, extrudeSettings);

  // 调整几何体位置和方向
  const extrudeMesh = new Three.Mesh(
    extrudeGeometry,
    new Three.MeshBasicMaterial({
      color: 0x00ff00,
      transparent: true,
      opacity: 0.5,
    })
  );

  // 根据相机方向调整挤出方向
  extrudeMesh.lookAt(camera.position);

  // 执行布尔减法操作
  try {
    // 从场景中移除当前模型
    scene.remove(mesh);

    // 执行CSG减法操作
    const csgResult = CSG.subtract(mesh, extrudeMesh);

    // 更新模型
    mesh = csgResult;
    scene.add(mesh);

    // 清除绘制轨迹
    clearDrawingPath();
  } catch (error) {
    console.error("切割操作失败:", error);
    // 恢复原始模型
    scene.add(mesh);
  }
}

// 重置模型
function resetModel() {
  scene.remove(mesh);
  mesh = originalMesh.clone();
  scene.add(mesh);
}

onMounted(() => {
  init();
});
</script>
<template>
  <div class="mianContainer" id="scene">
    <div
      class="openClip"
      @click="enableClipping"
      :class="{ disabled: isClipping }"
    >
      开启切割
    </div>
    <div
      class="openClip"
      @click="disableClipping"
      :class="{ disabled: !isClipping }"
    >
      关闭切割
    </div>
    <div class="openClip" @click="resetModel">重置模型</div>
    <div v-if="isClipping" class="clippingTip">
      按住鼠标在模型上绘制闭合区域，释放鼠标后将挖去该区域
    </div>
  </div>
</template>
<style lang="scss" scoped>
.mianContainer {
  width: 100%;
  height: calc(100vh - 100px);
  position: relative;
}
.openClip {
  height: 30px;
  width: 100px;
  background-color: #000;
  color: #fff;
  border-radius: 5px;
  cursor: pointer;
  text-align: center;
  line-height: 30px;
  margin-top: 10px;
}
.clippingTip {
  position: absolute;
  top: 10px;
  right: 10px;
  background-color: rgba(0, 0, 0, 0.7);
  color: white;
  padding: 10px;
  border-radius: 5px;
}
</style>
